﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestSocektServer
{
    /// <summary>
    /// System.Net.WebSockets.ClientWebSocket的帮助类
    /// </summary>
    public class WebSocketClientHelper : IDisposable
    {
        /// <summary>
        /// 实例化
        /// </summary>
        /// <param name="url">链接地址 示例:"ws://127.0.0.1:8888"</param>
        /// <param name="encoding">字符编码,null=UTF-8</param>
        public WebSocketClientHelper(string url, Encoding encoding = null)
        {
            clientWebSocket = new System.Net.WebSockets.ClientWebSocket();
            cancellationToken = new System.Threading.CancellationToken();
            this.url = url;
            this.encoding = encoding ?? Encoding.UTF8;
        }

        /// <summary>
        /// 字符编码
        /// </summary>
        public Encoding encoding { get; set; }

        /// <summary>
        /// 客户端
        /// </summary>
        public System.Net.WebSockets.ClientWebSocket clientWebSocket { get; }
        System.Threading.CancellationToken cancellationToken { get; }
        /// <summary>
        /// 链接地址
        /// </summary>
        string url { get; }

        /// <summary>
        /// 服务器回发消息时触发
        /// </summary>
        public event Action<string> ReceiveMessageEvent;

        /// <summary>
        /// 触发 ReceiveMessageEvent 事件
        /// </summary>
        /// <param name="Message">服务端发送的消息</param>
        protected virtual void OnReceiveMessageEvent(string Message)
        {
            if (ReceiveMessageEvent != null)
            {
                ReceiveMessageEvent(Message);
            }
        }

        /// <summary>
        /// 服务器回发消息byte[]时触发
        /// </summary>
        public event Action<byte[]> ReceiveByteEvent;

        /// <summary>
        /// 触发 ReceiveByteEvent 事件
        /// </summary>
        /// <param name="array">服务端发送的消息</param>
        protected virtual void OnReceiveByteEvent(byte[] array)
        {
            if (ReceiveByteEvent != null)
            {
                ReceiveByteEvent(array);
            }
        }

        /// <summary>
        /// 服务器关闭时时触发
        /// </summary>
        public event Action ReceiveCloseEvent;

        /// <summary>
        /// 触发 ReceiveCloseEvent 事件
        /// </summary>
        protected virtual void OnReceiveCloseEvent()
        {
            if (ReceiveCloseEvent != null)
            {
                ReceiveCloseEvent();
            }
        }

        /// <summary>
        /// 是否已经打开链接
        /// </summary>
        public bool IsOpen { get; private set; }

        /// <summary>
        /// 开启
        /// </summary>
        public virtual void Start()
        {
            if (IsOpen)
            {
                return;
            }
            Task task = clientWebSocket.ConnectAsync(new Uri(url), cancellationToken);
            task.Wait();
            IsOpen = true;

            Task.Run(() =>
            {
                StringBuilder stringBuilder = new StringBuilder();
                System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();
                var arraySegment = new ArraySegment<byte>(new byte[4096]);
                while (IsOpen)
                {
                    var taskMessage = clientWebSocket.ReceiveAsync(arraySegment, cancellationToken).ContinueWith(result =>
                    {
                        try
                        {
                            switch (result.Result.MessageType)
                            {
                                case System.Net.WebSockets.WebSocketMessageType.Text:
                                    {
                                        stringBuilder.Append(encoding.GetString(arraySegment.Array, 0, result.Result.Count));
                                        if (result.Result.EndOfMessage)
                                        {
                                            OnReceiveMessageEvent(stringBuilder.ToString());
                                            stringBuilder.Clear();
                                        }
                                    }
                                    break;
                                case System.Net.WebSockets.WebSocketMessageType.Binary:
                                    {
                                        memoryStream.Write(arraySegment.Array, 0, result.Result.Count);
                                        if (result.Result.EndOfMessage)
                                        {
                                            OnReceiveByteEvent(memoryStream.ToArray());
                                            memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
                                            memoryStream.SetLength(0);
                                        }
                                    }
                                    break;
                                case System.Net.WebSockets.WebSocketMessageType.Close:
                                    {
                                        OnReceiveCloseEvent();
                                    }
                                    break;
                            }
                        }
                        catch (Exception)
                        {
                            //服务端断开
                            OnReceiveCloseEvent();
                            IsOpen = false;
                        }
                    });
                    taskMessage.Wait();
                }
            });
        }

        /// <summary>
        /// 发送文本消息
        /// </summary>
        /// <param name="Message">消息</param>
        public async void SendMessage(string Message)
        {
            var arraySegment = new ArraySegment<byte>(encoding.GetBytes(Message));
            await clientWebSocket.SendAsync(arraySegment, System.Net.WebSockets.WebSocketMessageType.Text, true, cancellationToken);
        }

        /// <summary>
        /// 发送byte[]
        /// </summary>
        /// <param name="Message">消息</param>
        public async void SendMessage(params byte[] Message)
        {
            var arraySegment = new ArraySegment<byte>(Message);
            await clientWebSocket.SendAsync(arraySegment, System.Net.WebSockets.WebSocketMessageType.Binary, true, cancellationToken);
        }

        /// <summary>
        /// byte[]转字符串
        /// </summary>
        /// <param name="bytes">消息</param>
        public string ByteToMessage(byte[] bytes)
        {
            return encoding.GetString(bytes);
        }

        /// <summary>
        /// 关闭
        /// </summary>
        public void Dispose()
        {
            IsOpen = false;
            Task task = clientWebSocket.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, "关闭", cancellationToken);
            task.Wait(3000);
            clientWebSocket.Dispose();
        }
    }
}
